package com.gitee.generator.universal.common;

import cn.hutool.core.text.CharSequenceUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * DataTypeEnum
 * @since 2020-08-19 11:24
 * @author wsx
 */
@Slf4j
@Getter
@AllArgsConstructor
public enum DataTypeEnum {
    /** jdbc java 类型 */
    ARRAY(Types.ARRAY,null,null,null,false,1),
    BIT(Types.BIT,"BIT", "Integer","java.lang.Integer",false,1),
    TINYINT(Types.TINYINT,"TINYINT", DataTypeEnum.BIT.fieldType,DataTypeEnum.BIT.wholeFieldType,false,1),
    SMALLINT(Types.SMALLINT,"SMALLINT", DataTypeEnum.TINYINT.fieldType,DataTypeEnum.TINYINT.wholeFieldType,false,1),
    INTEGER(Types.INTEGER,"INTEGER", DataTypeEnum.BIT.fieldType,DataTypeEnum.TINYINT.wholeFieldType,false,1),
    INT(Types.INTEGER,"INT", DataTypeEnum.TINYINT.fieldType,DataTypeEnum.TINYINT.wholeFieldType,false,2),
    BIGINT(Types.BIGINT,"BIGINT", "Long","java.lang.Long",false,1),
    FLOAT(Types.FLOAT,"FLOAT", "Float","java.lang.Float",false,1),
    REAL(Types.REAL,"REAL", DataTypeEnum.FLOAT.fieldType,DataTypeEnum.FLOAT.wholeFieldType,false,1),
    DOUBLE(Types.DOUBLE,"DOUBLE", "Double","java.lang.Double",false,1),
    NUMERIC(Types.NUMERIC,"NUMERIC", "BigDecimal","java.math.BigDecimal",true,1),
    DECIMAL(Types.DECIMAL,"DECIMAL", DataTypeEnum.NUMERIC.fieldType,DataTypeEnum.NUMERIC.wholeFieldType,true,1),
    CHAR(Types.CHAR,"CHAR","String","java.lang.String",false,1),
    VARCHAR(Types.VARCHAR,"VARCHAR", DataTypeEnum.CHAR.fieldType,DataTypeEnum.CHAR.wholeFieldType,false,1),
    LONGVARCHAR(Types.LONGVARCHAR,"LONGVARCHAR", DataTypeEnum.CHAR.fieldType,DataTypeEnum.CHAR.wholeFieldType,false,1),
    DATE(Types.DATE,"DATE", "Date","java.util.Date",true,1),
    TIME(Types.TIME,"DATE", DataTypeEnum.DATE.fieldType,DataTypeEnum.DATE.wholeFieldType,true,1),
    TIMESTAMP(Types.TIMESTAMP,"TIMESTAMP", DataTypeEnum.DATE.fieldType,DataTypeEnum.DATE.wholeFieldType,true,1),
    BINARY(Types.BINARY,"BINARY", "Byte[]","java.lang.Byte",false,1),
    VARBINARY(Types.VARBINARY,"VARBINARY", DataTypeEnum.BINARY.fieldType,DataTypeEnum.BINARY.wholeFieldType,false,1),
    LONGVARBINARY(Types.LONGVARBINARY,"LONG VARBINARY", DataTypeEnum.BINARY.fieldType,DataTypeEnum.BINARY.wholeFieldType,false,1),
    NULL(Types.NULL,null,null,null,false,1),
    OTHER(Types.OTHER,null,null,null,false,1),
    BLOB(Types.BLOB,"BLOB", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,1),
    CLOB(Types.CLOB,"CLOB", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,1),
    BOOLEAN(Types.BOOLEAN,"BOOLEAN", "Boolean","java.lang.Boolean",false,1),
    CURSOR(-10,null,null,null,false,1), // Oracle
    UNDEFINED(Integer.MIN_VALUE + 1000,null,null,null,false,1),
    NVARCHAR(Types.NVARCHAR,"NVARCHAR", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,1), // JDK6
    NCHAR(Types.NCHAR,"NCHAR", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,1), // JDK6
    NCLOB(Types.NCLOB,"NCLOB", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,1), // JDK6
    STRUCT(Types.STRUCT,null,null,null,false,1),
    JAVA_OBJECT(Types.JAVA_OBJECT,null,null,null,false,1),
    DISTINCT(Types.DISTINCT,null,null,null,false,1),
    REF(Types.REF,null,null,null,false,1),
    DATALINK(Types.DATALINK,null,null,null,false,1),
    ROWID(Types.ROWID,null,null,null,false,1), // JDK6
    LONGNVARCHAR(Types.LONGNVARCHAR,"LONG NVARCHAR", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,1), // JDK6
    SQLXML(Types.SQLXML,null,null,null,false,1), // JDK6
    DATETIMEOFFSET(-155,null,null,null,false,1), // SQL Server 2008
    TIME_WITH_TIMEZONE(Types.TIME_WITH_TIMEZONE,"TIME_WITH_TIMEZONE", DataTypeEnum.DATE.fieldType,DataTypeEnum.DATE.wholeFieldType,true,1), // JDBC 4.2 JDK8
    TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE,"TIMESTAMP_WITH_TIMEZONE", DataTypeEnum.DATE.fieldType,DataTypeEnum.DATE.wholeFieldType,true,1), // JDBC 4.2 JDK8

    VARCHAR2(Types.VARCHAR,"VARCHAR2", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,2),
    MEDIUMTEXT(Types.LONGNVARCHAR,"MEDIUMTEXT", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,2),
    DATETIME(Types.TIMESTAMP,"DATETIME", DataTypeEnum.DATE.fieldType,DataTypeEnum.DATE.wholeFieldType,true,2),
    TEXT(Types.LONGNVARCHAR,"TEXT", DataTypeEnum.VARCHAR.fieldType,DataTypeEnum.VARCHAR.wholeFieldType,false,3),
    ENUM(Types.CHAR,"ENUM", DataTypeEnum.CHAR.fieldType,DataTypeEnum.CHAR.wholeFieldType,false,1);

    /** 数据库类型 */
    private final Integer dbTypeCode;
    /** 数据库类型 */
    private final String dbType;
    /** java类型 */
    private final String fieldType;
    /** java类型全路径 */
    private final String wholeFieldType;
    /** 是否需要导入 */
    private final boolean needImport;
    /** 排序 */
    private final Integer sort;

    private static final Map<Integer, List<DataTypeEnum>> DB_TYPE_CODE_MAP = new ConcurrentHashMap<>(100, 1.0F);
    private static final Map<String, DataTypeEnum> DB_TYPE_MAP = new ConcurrentHashMap<>(100, 1.0F);

    static {
        for (DataTypeEnum type : values()) {
            DB_TYPE_CODE_MAP.computeIfAbsent(type.dbTypeCode,k-> Collections.synchronizedList(new ArrayList<>())).add(type);
            if(null!=type.dbType){
                DB_TYPE_MAP.put(type.dbType, type);
            }
        }
        DB_TYPE_CODE_MAP.forEach((k,v)->{
            if(v.size()>1){
                v.sort(Comparator.comparing(DataTypeEnum::getSort));
            }
        });
    }
    /**
     * 通过数据库类型获取枚举
     * @param dbType 数据库类型
     * @return 枚举
     */
    public static DataTypeEnum getEnumByDbType(String dbType){
        if(CharSequenceUtil.isNotBlank(dbType)){
            dbType = dbType.replace("UNSIGNED","").trim().toUpperCase();
            return DB_TYPE_MAP.get(dbType);
        }
        log.error("未能解析的数据库类型：{}",dbType);
        return null;
    }
    /**
     * 通过数据库类型获取java属性
     * @param dbType 数据库类型
     * @return java属性
     */
    public static String getFieldType(String dbType){
        DataTypeEnum typeEnum = getEnumByDbType(dbType);
        if(null!=typeEnum){
            return typeEnum.fieldType;
        }
        return null;
    }
    /**
     * 通过数据库类型获取java类型全路径
     * @param dbType 数据库类型
     * @return java类型全路径
     */
    public static String getWholeFieldType(String dbType){
        DataTypeEnum typeEnum = getEnumByDbType(dbType);
        if(null!=typeEnum){
            return typeEnum.getWholeFieldType();
        }
        return null;
    }
    /**
     * todo 在类型上设置参数，一个dbTypeCode对应多个枚举时，根据参数选择一个合适的
     * 处理数据库类型转换
     * @param dbTypeCode 数据库类型
     * @return 数据库类型转换
     */
    public static String tranceDbType(Integer dbTypeCode,String typeName){
        String name = getByCode(dbTypeCode);
        return name!=null?name:typeName.split(" ")[0];
    }
    /**  */
    public static String getByCode(Integer dbTypeCode){
        List<DataTypeEnum> enums = DB_TYPE_CODE_MAP.get(dbTypeCode);
        DataTypeEnum res = null;
        if(null!= enums && enums.size()==1){
            res = enums.get(0);
        }else if(DataTypeEnum.INTEGER.dbTypeCode.equals(dbTypeCode)){
            res = DataTypeEnum.INTEGER;
        }else if(DataTypeEnum.BIT.dbTypeCode.equals(dbTypeCode)){
            res = DataTypeEnum.TINYINT;
        }else if(DataTypeEnum.TIMESTAMP.dbTypeCode.equals(dbTypeCode)){
            res = DataTypeEnum.TIMESTAMP;
        }else if(DataTypeEnum.VARCHAR.dbTypeCode.equals(dbTypeCode)){
            res = DataTypeEnum.VARCHAR;
        }else if(DataTypeEnum.TEXT.dbTypeCode.equals(dbTypeCode)){
            res = DataTypeEnum.TEXT;
        }
        return null==res?null:res.dbType;
    }
    public static boolean needImport(String dbType){
        DataTypeEnum typeEnum = getEnumByDbType(dbType);
        if(null!=typeEnum){
            return typeEnum.needImport;
        }
        return false;
    }
}
